
#ifndef __ENTITY_H__
#define __ENTITY_H__

#include "BBox.h"
#include "Geometry.h"
#include <d3d9types.h>

class Octree;

class Entity
{
public:

	// Constructor
	Entity();
	Entity(Entity *_parent, const string &_name = "Model");

	// Returns Parent Entity
	Entity *Parent() { return parent; }

	// Returns the Children vector
	vector<Entity*> &Children() { return children; }

	// Clears the children (recursive)
	void ClearChildren();

	// Returns the Geometry
	Geometry *Geom() const;
	SubdivisionGeom *SubGeom() const;
	BillboardGeom *BBGeom() const;
	TerrainGeom *Terrain() const;

	// Creates a geometry if not already created, 
	// will not override any other geometry already generated
	SubdivisionGeom *CreateSubGeom();
	BillboardGeom *CreateBBGeom(bool isFixedPoint = true);
	TerrainGeom *CreateTerrainGeom(vector<vector<uchar>> &hMap, ushort numDivisions = 5, Level detail = Med);

	// Set the geometry
	Geometry *SetGeom(Geometry *geom);

	// Recursively renders entity and sub-entities and there geometry
	void Render();

	// Recursively updates transform, based on velocity
	void UpdateTransform();

	// Returns Transforms
	Vector3 Position() const { return position; }
	Euler3 Rotation() const { return rotation; }

	// Changes the transforms
	void Translate(Real3 vec);
	void Rotate(Real3 rot);

	// Returns the matrix with the transforms
	D3DXMATRIX *WorldMatrix(); 

	// Sets the transforms
	void SetPosition(Vector3 _position);
	void SetRotation(Euler3 _rotation);

	// Sets the material
	void SetMaterial(Color diffuse  = __black, Color ambient  = __black,
		               Color specular = __black, Color emissive = __black,
									 float power = 0);

	// Loads the material
	void LoadMaterial();

	// Clears old box, and regenerates from geometry
	void GenerateBox();

	// Add Points
	void AddBBoxPoint(const Real3 &p);
	void AddOctreeTri(const Real3 a, const Real3 b, const Real3 c);
	
	// Returns the bounding box base
	BBox *Box() { return &baseBox; }

	// Gets the box for the entire entity tree
	BBox TreeBox();

	// For drawing bboxes
	uint GetBBoxVertsSize();
	void GetBBoxVerts(vector<Real3> &ptr);

	// Recursively Subdivides models
	void IncreaseSubdivision();
	void DecreaseSubdivision();

	// Writes to file
	void Write(FileStream &file);
	void WriteTree(string dir, bool binary);

	// Reads from file
	void Read(FileStream &file);

	// Sets the alpha value of all the vertices
	void SetAlpha(float alpha);

	// Gravity
	float GetGravityModifier() { return gravMod; }
	void SetGravityModifier(float g) { gravMod = g; }

	// Velocity
	Vector3 GetLinearVelocity() { return linearVel; }
	Euler3 GetRotationalVelocity() { return rotationalVel; }
	void SetLinearVelocity(Vector3 vel) { linearVel = vel; }
	void SetRotationalVelocity(Euler3 vel) { rotationalVel = vel; }

	// Acceleration
	void AccelerateLinearVelocity(Vector3 acc) { linearVel += acc; }
	void AccelerateRotationalVelocity(Euler3 acc) { rotationalVel += acc; }

	// Collision Test - tests this entity against all entities & their children
	// must make sure to test that this has an octree
	bool CollisionTest(const Entity *entity) const;

	// Checks for octree
	bool HasOctree() const { return octree != 0; }

private:

	// Parent entity
	Entity *parent;

	// Name
	string name;

	// Children
	vector<Entity*> children;

	// Geometry
	Geometry *geometry;

	// Bounding Box
	BBox baseBox;
	//BBox *rotBox;
	
	// Octree (for collision detection)
	Octree *octree;
	void GenerateOctree();

	// Transforms
	Vector3 position;
	Euler3 rotation;
	bool updateTransform;
	bool updateRotation;
	
	// Velocity
	Vector3 linearVel;
	Euler3 rotationalVel;

	// Gravity modifier
	float gravMod;

	// Material
	D3DMATERIAL9 material; 

	// World Matrix
	D3DXMATRIX *worldMatrix; 
	D3DXMATRIX *rotationMatrix; 

	// Adds a child to the children array
	void AddChild(Entity *child);
	
	// Collision Checks
	//friend bool CheckCollisions(Entity *entA, Entity *entB);

	friend class Engine;
};

#endif // __ENTITY_H__
